// Copyright (c) 2024 Go-Frame-Lite
// Go-Frame-Lite is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//         http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.

package crypto

import (
	"crypto/rand"
	"fmt"

	"github.com/tjfoc/gmsm/sm2"
	"github.com/tjfoc/gmsm/sm3"
	"github.com/tjfoc/gmsm/sm4"
)

// SM3Hash SM3哈希计算
// SM3是中国国家密码管理局发布的商用密码哈希算法，输出长度为256位（32字节）
// 算法特点：安全性高、抗碰撞性强、适用于各种安全应用场景
// 
// 参数:
//   data: 要计算哈希的原始数据字节切片
// 
// 返回值:
//   []byte: 32字节的SM3哈希值
//   error: 错误信息（无错误时为nil）
func SM3Hash(data []byte) []byte {
	hash := sm3.New()
	hash.Write(data)
	return hash.Sum(nil)
}

// SM3HashHex SM3哈希计算并返回十六进制字符串
// 将SM3哈希结果转换为可读的十六进制字符串格式
// 
// 参数:
//   data: 要计算哈希的原始数据字节切片
// 
// 返回值:
//   string: 64字符的十六进制哈希字符串（32字节的十六进制表示）
func SM3HashHex(data []byte) string {
	hash := SM3Hash(data)
	return fmt.Sprintf("%x", hash)
}

// SM3HashString 对字符串进行SM3哈希计算
// 直接对字符串进行SM3哈希计算，返回十六进制格式结果
// 
// 参数:
//   data: 要计算哈希的原始字符串
// 
// 返回值:
//   string: 64字符的十六进制哈希字符串
func SM3HashString(data string) string {
	return SM3HashHex([]byte(data))
}

// SM4Encrypt SM4加密（ECB模式）
// SM4是中国国家密码管理局发布的商用密码对称加密算法，分组长度为128位（16字节）
// ECB模式（电子密码本模式）：每个数据块独立加密，适合并行处理但安全性较低
// 
// 参数:
//   key: 16字节的SM4加密密钥
//   plaintext: 要加密的明文数据
// 
// 返回值:
//   []byte: 加密后的密文数据（长度是16字节的倍数）
//   error: 错误信息
func SM4Encrypt(key, plaintext []byte) ([]byte, error) {
	if len(key) != 16 {
		return nil, fmt.Errorf("SM4密钥长度必须为16字节")
	}

	cipher, err := sm4.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("创建SM4密码器失败: %w", err)
	}

	// 填充明文到16字节的倍数
	paddedText := pkcs7Pad(plaintext, cipher.BlockSize())
	ciphertext := make([]byte, len(paddedText))

	// 使用ECB模式加密
	for i := 0; i < len(paddedText); i += cipher.BlockSize() {
		cipher.Encrypt(ciphertext[i:i+cipher.BlockSize()], paddedText[i:i+cipher.BlockSize()])
	}

	return ciphertext, nil
}

// SM4Decrypt SM4解密（ECB模式）
// 对ECB模式加密的SM4密文进行解密
// 
// 参数:
//   key: 16字节的SM4解密密钥（必须与加密时使用的密钥相同）
//   ciphertext: 要解密的密文数据
// 
// 返回值:
//   []byte: 解密后的明文数据
//   error: 错误信息
func SM4Decrypt(key, ciphertext []byte) ([]byte, error) {
	if len(key) != 16 {
		return nil, fmt.Errorf("SM4密钥长度必须为16字节")
	}

	cipher, err := sm4.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("创建SM4密码器失败: %w", err)
	}

	if len(ciphertext)%cipher.BlockSize() != 0 {
		return nil, fmt.Errorf("密文长度必须是块大小的倍数")
	}

	plaintext := make([]byte, len(ciphertext))

	// 使用ECB模式解密
	for i := 0; i < len(ciphertext); i += cipher.BlockSize() {
		cipher.Decrypt(plaintext[i:i+cipher.BlockSize()], ciphertext[i:i+cipher.BlockSize()])
	}

	// 去除填充
	return pkcs7Unpad(plaintext, cipher.BlockSize())
}

// SM4EncryptWithIV SM4 CBC模式加密
// CBC模式（密码块链接模式）：每个数据块与前一个密文块进行异或操作后再加密
// 安全性高于ECB模式，适合需要更高安全性的应用场景
// 
// 参数:
//   key: 16字节的SM4加密密钥
//   iv: 16字节的初始化向量（IV），用于第一个数据块的加密
//   plaintext: 要加密的明文数据
// 
// 返回值:
//   []byte: 加密后的密文数据
//   error: 错误信息
func SM4EncryptWithIV(key, iv, plaintext []byte) ([]byte, error) {
	if len(key) != 16 {
		return nil, fmt.Errorf("SM4密钥长度必须为16字节")
	}
	if len(iv) != 16 {
		return nil, fmt.Errorf("SM4 IV长度必须为16字节")
	}

	cipher, err := sm4.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("创建SM4密码器失败: %w", err)
	}

	// 填充明文
	paddedText := pkcs7Pad(plaintext, cipher.BlockSize())
	ciphertext := make([]byte, len(paddedText))

	// CBC模式加密
	prevBlock := make([]byte, cipher.BlockSize())
	copy(prevBlock, iv)

	for i := 0; i < len(paddedText); i += cipher.BlockSize() {
		// 异或操作
		for j := 0; j < cipher.BlockSize(); j++ {
			paddedText[i+j] ^= prevBlock[j]
		}

		cipher.Encrypt(ciphertext[i:i+cipher.BlockSize()], paddedText[i:i+cipher.BlockSize()])
		copy(prevBlock, ciphertext[i:i+cipher.BlockSize()])
	}

	return ciphertext, nil
}

// SM4DecryptWithIV SM4 CBC模式解密
// 对CBC模式加密的SM4密文进行解密
// 
// 参数:
//   key: 16字节的SM4解密密钥（必须与加密时使用的密钥相同）
//   iv: 16字节的初始化向量（必须与加密时使用的IV相同）
//   ciphertext: 要解密的密文数据
// 
// 返回值:
//   []byte: 解密后的明文数据
//   error: 错误信息
func SM4DecryptWithIV(key, iv, ciphertext []byte) ([]byte, error) {
	if len(key) != 16 {
		return nil, fmt.Errorf("SM4密钥长度必须为16字节")
	}
	if len(iv) != 16 {
		return nil, fmt.Errorf("SM4 IV长度必须为16字节")
	}
	if len(ciphertext)%16 != 0 {
		return nil, fmt.Errorf("密文长度必须是16字节的倍数")
	}

	cipher, err := sm4.NewCipher(key)
	if err != nil {
		return nil, fmt.Errorf("创建SM4密码器失败: %w", err)
	}

	plaintext := make([]byte, len(ciphertext))

	// CBC模式解密
	prevBlock := make([]byte, cipher.BlockSize())
	copy(prevBlock, iv)

	for i := 0; i < len(ciphertext); i += cipher.BlockSize() {
		cipher.Decrypt(plaintext[i:i+cipher.BlockSize()], ciphertext[i:i+cipher.BlockSize()])

		// 异或操作
		for j := 0; j < cipher.BlockSize(); j++ {
			plaintext[i+j] ^= prevBlock[j]
		}

		copy(prevBlock, ciphertext[i:i+cipher.BlockSize()])
	}

	// 去除填充
	return pkcs7Unpad(plaintext, cipher.BlockSize())
}

// SM2Encrypt SM2公钥加密
// SM2是中国国家密码管理局发布的商用密码非对称加密算法，基于椭圆曲线密码学
// 使用公钥加密，私钥解密，适合安全传输对称密钥或小量数据
// 
// 参数:
//   publicKey: SM2公钥对象，用于加密数据
//   plaintext: 要加密的明文数据
// 
// 返回值:
//   []byte: 加密后的密文数据
//   error: 错误信息
func SM2Encrypt(publicKey *sm2.PublicKey, plaintext []byte) ([]byte, error) {
	ciphertext, err := sm2.Encrypt(publicKey, plaintext, rand.Reader, sm2.C1C3C2)
	if err != nil {
		return nil, fmt.Errorf("SM2加密失败: %w", err)
	}
	return ciphertext, nil
}

// SM2Decrypt SM2私钥解密
// 使用SM2私钥对密文进行解密
// 
// 参数:
//   privateKey: SM2私钥对象，用于解密数据
//   ciphertext: 要解密的密文数据
// 
// 返回值:
//   []byte: 解密后的明文数据
//   error: 错误信息
func SM2Decrypt(privateKey *sm2.PrivateKey, ciphertext []byte) ([]byte, error) {
	plaintext, err := sm2.Decrypt(privateKey, ciphertext, sm2.C1C3C2)
	if err != nil {
		return nil, fmt.Errorf("SM2解密失败: %w", err)
	}
	return plaintext, nil
}

// SM2Sign SM2签名
// 使用SM2私钥对数据进行数字签名，生成数字签名
// 签名可以验证数据的完整性和来源真实性
// 
// 参数:
//   privateKey: SM2私钥对象，用于生成签名
//   data: 要签名的原始数据
// 
// 返回值:
//   []byte: 生成的数字签名
//   error: 错误信息
func SM2Sign(privateKey *sm2.PrivateKey, data []byte) ([]byte, error) {
	signature, err := privateKey.Sign(rand.Reader, data, nil)
	if err != nil {
		return nil, fmt.Errorf("SM2签名失败: %w", err)
	}
	return signature, nil
}

// SM2Verify SM2验签
// 使用SM2公钥验证数字签名的有效性
// 
// 参数:
//   publicKey: SM2公钥对象，用于验证签名
//   data: 原始数据
//   signature: 要验证的数字签名
// 
// 返回值:
//   bool: 签名验证结果（true表示验证成功，false表示验证失败）
func SM2Verify(publicKey *sm2.PublicKey, data, signature []byte) bool {
	return publicKey.Verify(data, signature)
}

// GenerateSM4Key 生成随机的SM4密钥
// 使用密码学安全的随机数生成器生成16字节的SM4密钥
// 
// 返回值:
//   []byte: 16字节的随机SM4密钥
//   error: 错误信息
func GenerateSM4Key() ([]byte, error) {
	key := make([]byte, 16)
	_, err := rand.Read(key)
	if err != nil {
		return nil, fmt.Errorf("生成SM4密钥失败: %w", err)
	}
	return key, nil
}

// GenerateSM4IV 生成随机的SM4 IV
// 生成16字节的初始化向量（IV），用于CBC模式的加密操作
// 
// 返回值:
//   []byte: 16字节的随机IV
//   error: 错误信息
func GenerateSM4IV() ([]byte, error) {
	iv := make([]byte, 16)
	_, err := rand.Read(iv)
	if err != nil {
		return nil, fmt.Errorf("生成SM4 IV失败: %w", err)
	}
	return iv, nil
}

// SM4KeyToHex SM4密钥转换为十六进制字符串
// 将16字节的SM4密钥转换为32字符的十六进制字符串，便于存储和传输
// 
// 参数:
//   key: 16字节的SM4密钥
// 
// 返回值:
//   string: 32字符的十六进制字符串
func SM4KeyToHex(key []byte) string {
	return fmt.Sprintf("%x", key)
}

// SM4KeyFromHex 从十六进制字符串解析SM4密钥
// 将32字符的十六进制字符串解析为16字节的SM4密钥
// 
// 参数:
//   hexStr: 32字符的十六进制字符串
// 
// 返回值:
//   []byte: 16字节的SM4密钥
//   error: 错误信息
func SM4KeyFromHex(hexStr string) ([]byte, error) {
	if len(hexStr) != 32 {
		return nil, fmt.Errorf("SM4密钥十六进制字符串长度必须为32字符")
	}
	
	key := make([]byte, 16)
	for i := 0; i < 16; i++ {
		_, err := fmt.Sscanf(hexStr[i*2:i*2+2], "%02x", &key[i])
		if err != nil {
			return nil, fmt.Errorf("解析SM4密钥失败: %w", err)
		}
	}
	return key, nil
}

// pkcs7Pad PKCS7填充
// PKCS7填充算法：将数据填充到块大小的倍数
// 填充规则：添加n个值为n的字节，其中n是填充的字节数
// 
// 参数:
//   data: 要填充的原始数据
//   blockSize: 块大小（通常为16字节）
// 
// 返回值:
//   []byte: 填充后的数据
func pkcs7Pad(data []byte, blockSize int) []byte {
	padding := blockSize - len(data)%blockSize
	padText := make([]byte, padding)
	for i := range padText {
		padText[i] = byte(padding)
	}
	return append(data, padText...)
}

// pkcs7Unpad PKCS7去除填充
// 从填充后的数据中去除PKCS7填充
// 
// 参数:
//   data: 填充后的数据
//   blockSize: 块大小（通常为16字节）
// 
// 返回值:
//   []byte: 去除填充后的原始数据
//   error: 错误信息
func pkcs7Unpad(data []byte, blockSize int) ([]byte, error) {
	if len(data) == 0 {
		return nil, fmt.Errorf("数据为空")
	}
	
	padding := int(data[len(data)-1])
	if padding < 1 || padding > blockSize {
		return nil, fmt.Errorf("填充长度无效")
	}
	
	for i := len(data) - padding; i < len(data); i++ {
		if int(data[i]) != padding {
			return nil, fmt.Errorf("填充数据无效")
		}
	}
	
	return data[:len(data)-padding], nil
}

// SimpleEncrypt 简单的对称加密（使用SM4和随机密钥）
// 自动生成随机密钥和IV，使用SM4 CBC模式进行加密
// 适合需要快速加密且不需要管理密钥的场景
// 
// 参数:
//   plaintext: 要加密的明文数据
// 
// 返回值:
//   []byte: 加密后的密文数据
//   []byte: 32字节的密钥和IV组合（前16字节为密钥，后16字节为IV）
//   error: 错误信息
func SimpleEncrypt(plaintext []byte) ([]byte, []byte, error) {
	key, err := GenerateSM4Key()
	if err != nil {
		return nil, nil, err
	}
	
	iv, err := GenerateSM4IV()
	if err != nil {
		return nil, nil, err
	}
	
	ciphertext, err := SM4EncryptWithIV(key, iv, plaintext)
	if err != nil {
		return nil, nil, err
	}
	
	return ciphertext, append(key, iv...), nil
}

// SimpleDecrypt 简单的对称解密
// 使用SimpleEncrypt生成的密钥和IV组合进行解密
// 
// 参数:
//   ciphertext: 要解密的密文数据
//   keyAndIV: 32字节的密钥和IV组合（前16字节为密钥，后16字节为IV）
// 
// 返回值:
//   []byte: 解密后的明文数据
//   error: 错误信息
func SimpleDecrypt(ciphertext, keyAndIV []byte) ([]byte, error) {
	if len(keyAndIV) != 32 {
		return nil, fmt.Errorf("密钥和IV组合长度必须为32字节")
	}
	
	key := keyAndIV[:16]
	iv := keyAndIV[16:]
	
	return SM4DecryptWithIV(key, iv, ciphertext)
}